/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar.p071;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.JsonObject;
import com.lucky_byte.pay.jar.Field;
import com.lucky_byte.pay.jar.Packet;
import com.lucky_byte.pay.jar.Runtime;

/**
 * 银联 ACP 报文
 */
public class Packet071 extends Packet
{
	private static final Logger logger = LogManager.getLogger();

	private static final String ATTRS_PATH =
			Runtime.pathFor("/etc/071.properties");
	private static final Map<String, Field.Attr> ATTRS =
			Field.attrsFromPath(ATTRS_PATH);

	private Map<String, String> fset;

	public Packet071() {
		this.fset = new HashMap<>();
	}

	@Override
	public boolean has(String name) {
		return fset.containsKey(name);
	}

	@Override
	public String get(String name) {
		return fset.get(name);
	}

	@Override
	public boolean set(String name, String value) {
		if (name == null) {
			return false;
		}
		if (value == null) {
			fset.remove(name);
			return true;
		}
		return this.setField(name, value);
	}

	@Override
	public int count() {
		return fset.size();
	}

	@Override
	public List<String> keySet() {
		List<String> keyset = new ArrayList<>();
		for (String key : fset.keySet()) {
			keyset.add(key);
		}
		return keyset;
	}

	@Override
	public void remove(String name) {
		this.fset.remove(name);
	}

	@Override
	public void clear() {
		this.fset.clear();
	}

	@Override
	public Packet clone() {
		Packet071 packet = new Packet071();
		for (String key : this.fset.keySet()) {
			packet.fset.put(key, this.fset.get(key));
		}
		return packet;
	}

	@Override
	public JsonObject toJson() {
		JsonObject json_object = new JsonObject();
		for (String key : fset.keySet()) {
			json_object.addProperty(key, fset.get(key));
		}
		return json_object;
	}

	/**
	 * 解析报文
	 */
	public boolean parse(String data) {
		if (data == null || data.trim().length() == 0) {
			logger.warn("解析银联 ACP 报文错误，报文为空.");
			return false;
		}
		String[] array = data.split("&");
		for (String entry : array) {
			String[] kv = entry.split("=", 2);
			if (kv.length < 2) {
				logger.error("解析银联 ACP 报文错误，报文格式无效.");
				return false;
			}
			logger.info("读取字段[{}][{}]", kv[0], kv[1]);
			this.setField(kv[0], kv[1]);
		}
		logger.info("共读取[{}]个字段.", array.length);
		return true;
	}

	/**
	 * 添加报文字段
	 */
	private boolean setField(String name, String value) {
		Field.Attr attr = ATTRS.get(name);
		if (attr == null) {
			logger.error("文件[{}]中未找到字段[{}]的配置.", ATTRS_PATH, name);
			return false;
		}
		// 验证数据长度是否有效
		if (value.length() > attr.maxlen) {
			logger.error("字段[{}]值[{}]的长度[{}]超出配置中最大长度[{}].",
					name, value, value.length(), attr.maxlen);
			return false;
		}
		// 定长字段需要进行补齐
		if (attr.llvar == 0 && value.length() < attr.maxlen) {
			StringBuilder builder = new StringBuilder();
			int pad_count = attr.maxlen - value.length();
			if (attr.align == Field.Attr.ALIGN_LEFT) {
				builder.append(value);
				for (int i = 0; i < pad_count; i++) {
					builder.append(Character.toChars(attr.padval));
				}
			} else {
				for (int i = 0; i < pad_count; i++) {
					builder.append(Character.toChars(attr.padval));
				}
				builder.append(value);
			}
			value = builder.toString();
		}
		// 如果配置了正则表达式，也需要验证
		if (attr.regex != null) {
			if (!value.matches(attr.regex)) {
				logger.error("字段[{}]内容[{}]不匹配[{}]，请检查.",
						name, value, attr.regex);
				return false;
			}
		}
		this.fset.put(name, value);
		return true;
	}

	/**
	 * 组建传输报文
	 */
	public Map<String, String> pack() {
		Map<String, String> result = new HashMap<>();

		for (String key : this.fset.keySet()) {
			String value = fset.get(key);
			if (!this.packField(result, key, value)) {
				return null;
			}
		}
		logger.info("共打包[{}]个字段.", this.fset.size());
		return result;
	}

	/**
	 * 打包单个字段
	 */
	private boolean packField(Map<String, String> result,
			String name, String value) {
		Field.Attr attr = ATTRS.get(name);
		if (value.length() > attr.maxlen) {
			logger.error("字段[{}]到内容长度[{}]超出了定义的最大长度[{}].",
					name, value.length(), attr.maxlen);
			return false;
		}
		logger.info("打包字段[{}][{}]", name, value);
		result.put(name, value);
		return true;
	}

}
